package org.bookshare.validator.report;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import org.benetech.event.EventListener;
import org.benetech.util.FileUtils;
import org.bookshare.document.beans.HasOPF;
import org.bookshare.document.beans.HasXML;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;


/**
 * Report engine that generates a static HTML report. Uses the NIMASReport.ftl
 * freemarker template to generate the content. Runs a list of ReportGenerators
 * to generate additional data prior to rendering the template.
 * @author Reuben Firmin
 */
public final class HTMLReportEngine implements ReportEngine {
    /**
     * List of generators to run prior to rendering the template.
     */
    private ReportGenerator[] generators = new ReportGenerator[] {
            new XMLStructureReportGenerator(),
            new StatisticsReportGenerator(),
            new FileContentsReportGenerator(),
    };


    /**
     * Default constructor.
     *
     */
    public HTMLReportEngine() {
    	// empty
    }

    /**
     * {@inheritDoc}
     */
    public void generateReport(final File outputDir, final String reportName, final ValidationMetadata report,
        final EventListener listener)
        throws IOException
    {
        // make sure we have a file we can write to
        if (!outputDir.canWrite()) {
        	throw new IOException("Cannot write to: "
        			+ outputDir.getAbsolutePath());
        }

        final File file = new File(outputDir.getAbsolutePath() + File.separator + reportName + ".html");
        if (!file.exists()) {
        	file.createNewFile();
        }
        if (!file.canWrite()) {
            throw new IOException("Cannot write to: " + file.getAbsolutePath());
        }

        final String resourceDirName = reportName + "_files";
        final File resourceDir = new File(outputDir.getAbsolutePath() + File.separator + resourceDirName
        		+ File.separator);
        if (!resourceDir.exists()) {
        	resourceDir.mkdirs();
        }
        if (!resourceDir.canWrite()) {
            throw new IOException("Cannot write to: "
            		+ resourceDir.getAbsolutePath());
        }

        FileUtils.copyResourcesFromClasspath("images", resourceDir);
        FileUtils.copyResourcesFromClasspath("js", resourceDir);
        FileUtils.copyResourcesFromClasspath("css", resourceDir);
        FileUtils.copyResourcesFromClasspath("html", resourceDir);

        // make sure the template can be loaded
        final Template reportTemplate = getTemplate();

        // run any report generators
        for (ReportGenerator generator : generators) {
            generator.addToReport(report);
        }

        final Map<String, Object> model = new HashMap<String, Object>();
        model.put("report", report);
        model.put("basePath", report.getDocumentSet().getFileBasePath());
        model.put("zipContents", report.getDocumentSet());
        model.put("opfDoc", ((HasOPF) report.getDocumentSet()).getOPFDocument());
        model.put("xmlDoc", ((HasXML) report.getDocumentSet()).getXMLDocument());
        model.put("seperator", File.separator);
        model.put("resourcePath", resourceDirName);
        model.put("dcMetadataKeys", ((Map) report.getModelMap().get("dcMetadata")).keySet());
        if (report.getModelMap().get("xMetadata") != null) {
        	model.put("xMetadataKeys", ((Map) report.getModelMap().get("xMetadata")).keySet());
        }
        model.put("random", new Random(System.currentTimeMillis()));

        final Writer out = new OutputStreamWriter(new FileOutputStream(file));
        try {
            reportTemplate.process(model, out);
        } catch (TemplateException e) {
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
        out.flush();
        listener.message("Report generated: " + file.getAbsolutePath());
    }

    /**
     * Get a handle on the freemarker template.
     * @return not null
     * @throws IOException if it can't be loaded
     */
    private Template getTemplate()
        throws IOException
    {
    	final PathMatchingResourcePatternResolver pmrpr = new PathMatchingResourcePatternResolver();
        final Configuration cfg = new Configuration();
        cfg.setDirectoryForTemplateLoading(
                pmrpr.getResource("classpath:templates").getFile());
        final BeansWrapper beanWrapper = new BeansWrapper();
        cfg.setObjectWrapper(beanWrapper);
        return cfg.getTemplate("validatorReport.ftl");
    }
}
